#include "txdnn_internal.h"
#include "macros/errors.h"


/*
 * TXDNN_GetDefaultLogger implementation
 */
TXDNNAPI txdnn::ILogger* TXDNN_GetDefaultLogger(const char* file_path, txdnn::ILogger::Level level) _TX_NOEXCEPT {
  if (!file_path) {
    static txdnn::StdLogger k_std_logger(level);
    return &k_std_logger;
  }

  static std::map<std::string, std::unique_ptr<txdnn::ILogger>> k_default_loggers;
  std::string path(file_path);

  static std::mutex k_loggers_mtx;
  std::lock_guard _(k_loggers_mtx);

  if (auto iter = k_default_loggers.find(path); iter != k_default_loggers.end()) {
    return iter->second.get();
  }

  return k_default_loggers
    .emplace(path, std::make_unique<txdnn::FileLogger>(level, std::make_unique<txdnn::FileFlusher>(path)))
    .first->second.get();
}


/* initLogger */
void initLogger(txdnn::ILogger* logger, std::string file_path, txdnn::ILogger::Level level) _TX_NOEXCEPT {
  if (file_path.empty()) {
    logger = TXDNN_GetDefaultLogger(nullptr, level);
  } else {
    logger = TXDNN_GetDefaultLogger(file_path.c_str(), level);
  }
}


/* operator work flow */
/*  1. txdnnCreate
    2. txdnnCreateOperatorDescriptor
    3. txdnnCreateTensorDescriptor
    4. txdnnSetTensorDescriptor
    5. memcpy : H to D
    6. txdnnOperatorForward/Inference
    7. memcpy : D to H
    8. txdnnDestroy
    9. txdnnDestroyTensorDescriptor
    10. txdnnDestroyOperatorDescriptor
    11. memory free
    
*/

// This function initializes the txDNN library and creates a handle to an opaque structure 
// holding the txDNN library context. 
// It allocates hardware resources on the host and device and 
// must be called prior to making any other txDNN library calls.
extern "C" txdnnStatus_t txdnnCreate(txdnnHandle_t* handle) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

// This function releases the resources used by the txDNN handle.
// This function is usually the last call made to txDNN with a particular handle. 
// Because txdnnCreate() allocates internal resources, 
// the release of those resources by calling txdnnDestroy() will implicitly call txdaDeviceSynchronize
extern "C" txdnnStatus_t txdnnDestroy(txdnnHandle_t handle) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

extern "C" txdnnStatus_t txdnnSetStream(txdnnHandle_t handle /*txdaStream_t streamId*/) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

extern "C" txdnnStatus_t txdnnGetStream(txdnnHandle_t handle /*txdaStream_t *streamId*/) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

// This function allocates memory in the descriptor 
// for a given descriptor type and at the location pointed by the descriptor.
extern "C" txdnnStatus_t txdnnBackendCreateDescriptor(txdnnBackendDescriptorType_t descriptorType,
                                                      txdnnBackendDescriptor_t* descriptor) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

// This function destroys instances of txdnnBackendDescriptor_t 
// that were previously created using txdnnBackendCreateDescriptor()
extern "C" txdnnStatus_t txdnnBackendDestroyDescriptor(txdnnBackendDescriptor_t descriptor) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

extern "C" txdnnStatus_t txdnnBackendInitialize(txdnnBackendDescriptor_t descriptor) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

// This function finalizes the memory pointed to by the descriptor. 
// The type of finalization is done depending on the descriptorType argument 
// with which the descriptor was created using txdnnBackendCreateDescriptor() 
// also checks all the attributes set between the create and finalize phase.
extern "C" txdnnStatus_t txdnnBackendFinalize(txdnnBackendDescriptor_t descriptor) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

extern "C" txdnnStatus_t txdnnBackendSetAttribute(txdnnBackendDescriptor_t descriptor,
                                                  txdnnBackendAttributeName_t attributeName,
                                                  txdnnBackendAttributeType_t attributeType, int64_t elementCount,
                                                  const void* arrayOfElements) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}

extern "C" txdnnStatus_t txdnnBackendGetAttribute(txdnnBackendDescriptor_t const descriptor,
                                                  txdnnBackendAttributeName_t attributeName,
                                                  txdnnBackendAttributeType_t attributeType,
                                                  int64_t requestedElementCount, int64_t* elementCount,
                                                  void* arrayOfElements) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}


// This function executes the given Engine Configuration Plan on the VariantPack 
// and the finalized ExecutionPlan on the data. 
// The data and the working space are encapsulated in the VariantPack.
extern "C" txdnnStatus_t txdnnBackendExecute(txdnnHandle_t handle, txdnnBackendDescriptor_t executionPlan,
                                             txdnnBackendDescriptor_t variantPack) {
  return txdnn::TxdnnTry([&] {
    // TODO
  });
}